package be.aquafin.klipdf.spatial.model;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import be.aquafin.klipdf.model.Objecttype;
import be.aquafin.klipdf.model.SpatialObject;

import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.index.strtree.STRtree;

public final class SpatialTreeModel {
	private static final Logger logger = LoggerFactory.getLogger(SpatialTreeModel.class);
	private final Map<Objecttype, STRtree> strForest = new ConcurrentHashMap<Objecttype, STRtree>();
	private static final double BUFFER = 5.0;

	public void store(Objecttype objecttype, List<? extends SpatialObject> spatialObjects) {
		if(objecttype == null) {
			throw new IllegalArgumentException("[objecttype] cannot be null");
		}
		if (spatialObjects == null) {
			throw new IllegalArgumentException("[SpatialObjects] cannot be null");
		}

		logger.info("#" + objecttype + ":" + spatialObjects.size());
		strForest.remove(objecttype);
		STRtree strTree = new STRtree();
		this.storeSpatialObjects(strTree, spatialObjects);
		strForest.put(objecttype, strTree);
	}

	private void storeSpatialObjects(STRtree strTree, List<? extends SpatialObject> spatialObjects) {
		for (int i = 0; i < spatialObjects.size(); i++) {
			SpatialObject spatialObject = spatialObjects.get(i);
			Envelope env = spatialObject.getGeometry().getEnvelopeInternal();
			strTree.insert(env, spatialObject);
		}
		strTree.build();
	}

	@SuppressWarnings("unchecked")
	public List<? extends SpatialObject> search(Objecttype objecttype, Geometry geometry) {
		if(objecttype == null) {
			throw new IllegalArgumentException("[objecttype] cannot be null");
		}
		if (geometry == null) {
			throw new IllegalArgumentException("[geometry] is null");
		}
		if (strForest.containsKey(objecttype)) {
			STRtree strTree = strForest.get(objecttype);
			return strTree.query(geometry.buffer(BUFFER).getEnvelopeInternal());
		}
		return null;
	}
}
